import time
import torch
from torch import nn, optim
import torchvision
import d2lzh_pytorch as d2l
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

class AlexNet(nn.Module):
    def __init__(self):
        super(AlexNet, self).__init__()
        self.conv = nn.Sequential(
            # 这里采用MNIST-FASHION数据集，所以，输入为单通道
            # 输入1 * 224 * 224， 卷积核大小：11 * 11,数量96，步长4 ==》输出：96 * 54 * 54
            nn.Conv2d(1, 96, 11, 4),
            nn.ReLU(),
            # 输入：96 * 54 * 54，输出：96 * 26 * 26
            nn.MaxPool2d(3, 2), # kernel_size, stride
            # 减小卷积窗口，使用填充为2来使得输入与输出的高和宽一致，且增大输出通道数
            # 输入：96 * 26 * 26，卷积核大小：5 * 5， 步长1，padding2 ==》 输出：256 * 26 * 26
            nn.Conv2d(96, 256, 5, 1, 2),
            nn.ReLU(),
            # 输入：256 * 26 * 26，输出：256 * 12 * 12
            nn.MaxPool2d(3, 2),
            # 连续3个卷积层，且使用更小的卷积窗口。除了最后的卷积层外，进一步增大了输出通道数。
            # 前两个卷积层后不使用池化层来减小输入的高和宽
            # 输入：256 * 12 * 12，卷积核大小3 * 3，数量384，步长1，padding1 ==》输出：384 * 12 * 12
            nn.Conv2d(256, 384, 3, 1, 1),
            nn.ReLU(),
            # 输入：384 * 12 * 12，卷积核大小 3 * 3，数量384，步长1，padding1 ==》输出：384 * 12 * 12
            nn.Conv2d(384, 384, 3, 1, 1),
            nn.ReLU(),
            # 输入：384 * 12 * 12，卷积核大小 3 * 3，数量256，步长1，padding1 ==》输出：256 * 12 * 12
            nn.Conv2d(384, 256, 3, 1, 1),
            nn.ReLU(),
            # 输入：256 * 12 * 12，输出：256 * 5 * 5
            nn.MaxPool2d(3, 2)
        )
         # 这里全连接层的输出个数比LeNet中的大数倍。使用丢弃层来缓解过拟合
        self.fc = nn.Sequential(
            nn.Linear(256*5*5, 4096),
            nn.ReLU(),
            nn.Dropout(0.5),
            nn.Linear(4096, 4096),
            nn.ReLU(),
            nn.Dropout(0.5),
            # 输出层。由于这里使用Fashion-MNIST，所以用类别数为10，而非论文中的1000
            nn.Linear(4096, 10),
        )

    def forward(self, img):
        feature = self.conv(img)
        output = self.fc(feature.view(img.shape[0], -1))
        return output


net = AlexNet()
batch_size = 128
# 如出现“out of memory”的报错信息，可减小batch_size或resize
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=224)
lr, num_epochs = 0.001, 5
optimizer = torch.optim.Adam(net.parameters(), lr=lr)
d2l.train_ch5(net, train_iter, test_iter, batch_size, optimizer, device, num_epochs)
